package com.ordos.recruit.advice;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.ordos.recruit.module.user.exception.UserLoginException;
import com.wonders.commons.util.constant.BaseConstant;
import com.wonders.commons.util.exception.ApiException;
import com.wonders.commons.util.exception.GlobException;
import com.wonders.commons.util.model.ErrorDataInfo;
import com.wonders.commons.util.response.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

/**
 * 统一异常处理
 *
 * @author jk
 */
@ControllerAdvice
@SuppressWarnings("all")
@Order(0)
public class ApiControllerAdvice {
    private final Logger logger = LoggerFactory.getLogger(ApiControllerAdvice.class);

    /**
     * 运算异常
     *
     * @param e ArithmeticException
     * @return ApiResponse
     */
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    private ApiResponse arithmeticExceptionHandler(Exception e)
    {
        logger.error("运算异常", e);
        return ApiResponse.error("运算异常：" + ExceptionUtil.getMessage(e));
    }

    /**
     * ValidException
     *
     * @param ex MethodArgumentNotValidException
     * @return ApiResponse
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ApiResponse methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex)
    {
        //按需重新封装需要返回的错误信息
        StringBuilder sb = new StringBuilder();
        //解析原错误信息，封装后返回，此处返回非法的字段名称，原始值，错误信息
        for (FieldError error : ex.getBindingResult().getFieldErrors())
        {

            sb.append(error.getField()).append(":");
            sb.append(error.getDefaultMessage()).append(";");
        }
        return ApiResponse.error(sb.toString());
    }

    /**
     * ConstraintViolationException
     *
     * @param e ConstraintViolationException
     * @return ApiResponse
     */
    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ApiResponse constraintViolationExceptionHandler(ConstraintViolationException e)
    {
        logger.error("数据异常", e);
        return ApiResponse.error("数据异常：" + ExceptionUtil.getMessage(e));
    }

    /**
     * 拦截捕捉自定义异常 ApiException.class
     *
     * @param ex ApiException
     * @return ApiResponse
     */
    @ResponseBody
    @ExceptionHandler(value = ApiException.class)
    public ApiResponse apiExceptionErrorHandler(ApiException ex, HttpServletRequest request)
    {
        if (ex.getResponse().getCode().equals(ApiResponse.TOKEN_ERROR_CODE))
        {
            request.getSession().invalidate();
        }
        return ex.getResponse();
    }

    /**
     * 拦截捕捉自定义异常 UserLoginException.class
     *
     * @param ex UserLoginException
     * @return ApiResponse
     */
    @ResponseBody
    @ExceptionHandler(value = UserLoginException.class)
    public ApiResponse userLoginExceptionErrorHandler(UserLoginException userLoginException, HttpServletRequest request)
    {
        return ApiResponse.error(userLoginException.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ApiResponse httpMessageNotReadableExceptionHandler(HttpMessageNotReadableException httpMessageNotReadableException) {
        return ApiResponse.error(httpMessageNotReadableException.getMessage());
    }

    /**
     * 拦截捕捉自定义异常 GlobException.class
     *
     * @param ex GlobException
     * @return ApiResponse
     */
    @ResponseBody
    @ExceptionHandler(value = GlobException.class)
    public ApiResponse apiExceptionErrorHandler(GlobException e, HttpServletRequest request)
    {
        logger.error("请求'{}'异常", request.getRequestURI(), e);
        return ApiResponse.error(e.getMessage());
    }

    /**
     * 全局异常捕捉处理
     *
     * @param e Exception
     * @return ApiResponse
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ApiResponse exceptionHandler(Exception e, HttpServletRequest request)
    {
        logger.error("请求'{}'异常", request.getRequestURI(), e);
        return ApiResponse.systemError("系统异常");
    }

    /**
     * 返回拦截 正确返回  记录上传数据量
     *
     * @param body               返回结果
     * @param methodParameter    方法
     * @param mediaType          请求类型
     * @param aClass             Class
     * @param serverHttpRequest  serverHttpRequest
     * @param serverHttpResponse serverHttpResponse
     * @return ApiResponse
     */
    public Object beforeBodyWrite1(Object body, @Nullable MethodParameter methodParameter, @Nullable MediaType mediaType, @Nullable Class aClass, @Nullable ServerHttpRequest serverHttpRequest, @Nullable ServerHttpResponse serverHttpResponse)
    {
        try
        {
            if (body instanceof ApiResponse)
            {
                ApiResponse<?> apiResponse = (ApiResponse<?>) body;
                Object obj = apiResponse.getData();
                //记录数据上传情况
                String channelCode = serverHttpRequest.getHeaders().getFirst(BaseConstant.API_TOKEN_CHANNEL_CODE);
                String channelName=  serverHttpRequest.getHeaders().getFirst("Token-ChannelName");
                if (obj instanceof ErrorDataInfo)
                {
                    ErrorDataInfo info = (ErrorDataInfo) obj;
                    logger.info("'{}({})'调用接口'{}'上传成功'{}',失败'{}'",channelName,channelCode,serverHttpRequest.getURI().getPath(),info.getSuccessNum(),info.getErrorNum());
                }
            }
        } catch (Exception e)
        {
            logger.error("请求地址'{}',打印上传数据量失败:{}", (serverHttpRequest != null ? serverHttpRequest.getURI().getPath() : "未知"), e.getMessage());
        }
        return body;
    }
}